//
//  Framework for a raytracer
//  File: cylinder.cpp
//
//  Created for the Computer Science course "Introduction Computer Graphics"
//  taught at the University of Groningen by Tobias Isenberg.
//
//  Authors:
//	  Zhe Sun
//
//  This framework is inspired by and uses code of the raytracer framework of 
//  Bert Freudenberg that can be found at
//  http://isgwww.cs.uni-magdeburg.de/graphik/lehre/cg2/projekt/rtprojekt.html 
//

#include "cylinder.h"
#include <iostream>
#include <math.h>

Hit Cylinder::intersect(const Ray &ray)
{
    double a, b, c;
    switch(axis)
    {
        case X_INFINITE:
            a =		ray.D.y * ray.D.y + ray.D.z * ray.D.z;
                
            b = 2 * (	ray.D.y * (ray.O.y - center.y) + ray.D.z * (ray.O.z - center.z));
                        
            c = (ray.O.y - center.y) * (ray.O.y - center.y) + 
                        (ray.O.z - center.z) * (ray.O.z - center.z) - r * r;
            break;
        case Y_INFINITE:
            a = 	ray.D.x * ray.D.x + 
                        ray.D.z * ray.D.z;
                
            b = 2 * (	ray.D.x * (ray.O.x - center.x) + 
                        ray.D.z * (ray.O.z - center.z));
                        
            c =        	(ray.O.x - center.x) * (ray.O.x - center.x) + 
                        (ray.O.z - center.z) * (ray.O.z - center.z) - r * r;
            break;
        case Z_INFINITE:
            a = 	ray.D.x * ray.D.x + 
                        ray.D.y * ray.D.y; 
                
            b = 2 * (	
                        ray.D.x * (ray.O.x - center.x) + 
                        ray.D.y * (ray.O.y - center.y));
                        
            c =        	(ray.O.x - center.x) * (ray.O.x - center.x) + 
                        (ray.O.y - center.y) * (ray.O.y - center.y) - r * r;                    
            break;
    }    
                
    double discriminant = b * b - 4 * a * c;
    if(discriminant<0)
    {
        return Hit::NO_HIT();
    }
    
    double distance = (-b - sqrt(discriminant)) / (2 * a);
    if(distance<0)
    {
        return Hit::NO_HIT();
    }

    Point intersection = ray.O + ray.D * distance;
    Vector normal;
    double oneOverRadius = 1/r; 	
    switch(axis)
    {
        case X_INFINITE:
            normal.x = 0;
            normal.y = ( intersection.y - center.y ) * oneOverRadius; 
            normal.z = ( intersection.z - center.z ) * oneOverRadius;  
            break;
        case Y_INFINITE:
            normal.x = ( intersection.x - center.x ) * oneOverRadius; 
            normal.y = 0;
            normal.z = ( intersection.z - center.z ) * oneOverRadius;  
            break;
        case Z_INFINITE:
            normal.x = ( intersection.x - center.x ) * oneOverRadius; 
            normal.y = ( intersection.y - center.y ) * oneOverRadius; 
            normal.z = 0;
    }

    return Hit(distance, normal);
}
